package com.hsgene.gateway.support;

import com.google.common.collect.Maps;
import com.hsgene.authority.exception.AuthorityErrorCode;
import com.hsgene.common.ClientType;
import com.hsgene.common.util.result.ApiResult;
import com.hsgene.common.util.result.NullObject;
import com.hsgene.common.util.web.mapping.HsgeneRequestMappingHandlerMapping;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author wangbing
 * @version 1.0, 2018/11/9
 */
@Component
public class ClientRequestValidationSupport {

    private final static Logger LOGGER = LoggerFactory.getLogger(ClientRequestValidationSupport.class);

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private Environment environment;

    /**
     * 根据不同端别配置不同的验证方式
     */
    private Map<ClientType, List<ClientRequestFilter>> filterMap;

    private Map<String, String> ignoreUrlsRedisKeyMap = Maps.newHashMap();

    public ApiResult<NullObject> validate(HttpServletRequest request) {
        LOGGER.debug("------->验证客户端请求：开始");
        if (isIgnoreUserToken(null, request)) {
            return ApiResult.succ();
        }
        // 请求端识别
        ClientType clientType = parseClientType(request);
        if (clientType == null) {
            LOGGER.error("请求端识别失败，请传入有效的端别参数");
            return ApiResult.fail(AuthorityErrorCode.PLATFORM_PARAM_NOT_EXISTED_OR_ERROR);
        }
        // 请求接口是否需要登录
        if (isIgnoreUserToken(clientType, request)) {
            return ApiResult.succ();
        }

        // 根据请求端执行过滤逻辑
        List<ClientRequestFilter> filters = filterMap.get(clientType);
        ApiResult result = ApiResult.succ();
        if (!CollectionUtils.isEmpty(filters)) {
            for (ClientRequestFilter filter : filters) {
                if (filter.shouldFilter(request)) {
                    result = filter.run(request);
                    if (!result.isSucc()) {
                        break;
                    }
                }
            }
        }
        LOGGER.debug("------->验证客户端请求：结束-" + result.getDesc());
        return result;
    }

    public Map<ClientType, List<ClientRequestFilter>> getFilterMap() {
        return filterMap;
    }

    public void setFilterMap(Map<ClientType, List<ClientRequestFilter>> filterMap) {
        this.filterMap = filterMap;
    }

    public static ClientType parseClientType(HttpServletRequest request) {
        // 有platform标识参数，优先识别标识参数
        String platform = request.getParameter("platform");
        if (StringUtils.isNotBlank(platform)) {
            try {
                return ClientType.getEnum(Integer.valueOf(platform));
            } catch (Exception e) {
                LOGGER.error("platform参数映射错误，platform={}", platform);
                e.printStackTrace();
                return null;
            }
        }
        // 无platform标识参数，第二顺序识别请求头Authorization参数，有则标识为花生仁
        String authorization = request.getHeader("Authorization");
        if (StringUtils.isBlank(authorization)) {
            authorization = request.getHeader("authorization");
        }
        if (StringUtils.isNotBlank(authorization)) {
            return ClientType.PEANUT;
        }
        // 无platform标识参数，也无请求头Authorization参数，则判断请求参数userId和doctorId,有则标识为金琉璃
        String doctorId = request.getParameter("userId");
        if (StringUtils.isNotBlank(doctorId)) {
            return ClientType.GOLD_COLORED_GLASS;
        }
        doctorId = request.getParameter("doctorId");
        if (StringUtils.isNotBlank(doctorId)) {
            return ClientType.GOLD_COLORED_GLASS;
        }

        return null;
    }

    private boolean isIgnoreUserToken(ClientType clientType, HttpServletRequest request) {
        String requestMethod = request.getMethod();
        String requestURI = request.getRequestURI();

        int index = requestURI.indexOf("/", 1);
        String routeIndex = requestURI.substring(1, index);
        String realUri = requestURI.substring(index);
        String serviceId = environment.getProperty("zuul.routes." + routeIndex + ".serviceId");
        if (StringUtils.isBlank(serviceId)) {
            return true;
        }

        String ignoreUrlsRedisKey = ignoreUrlsRedisKeyMap.get(serviceId);
        if (StringUtils.isBlank(ignoreUrlsRedisKey)) {
            ignoreUrlsRedisKey = HsgeneRequestMappingHandlerMapping.formatIgnoreUrlsRedisKey(serviceId);
            ignoreUrlsRedisKeyMap.put(serviceId, ignoreUrlsRedisKey);
        }
        String wholeUri = requestMethod + " " + realUri;
        Set<String> allIgnoreUrls = redisTemplate.opsForSet().members(ignoreUrlsRedisKey + ":" + 0);
        if (!CollectionUtils.isEmpty(allIgnoreUrls)) {
            if (allIgnoreUrls.stream().anyMatch(wholeUri::matches)) {
                return true;
            }
        }
        if (clientType != null) {
            Set<String> platformIgnoreUrls = redisTemplate.opsForSet().members(ignoreUrlsRedisKey + ":" + clientType.getCode());
            if (!CollectionUtils.isEmpty(platformIgnoreUrls)) {
                return platformIgnoreUrls.stream().anyMatch(wholeUri::matches);
            }
        }

        return false;
    }
}
